using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using RoboTrocity.Code.ObjectStates;
using RoboTrocity.Code.Props;

namespace RoboTrocity {
    class Entity : MovableObject {
        private String name;
		private bool onSlope = false;
    	
		protected Dictionary<string, int> entityStats;

		/// <summary>
		/// This constructor initializes the Entity at (0,0).
		/// </summary>
		public Entity()
            : this(Point.Zero) {
			stateList = new List<ObjectState>();
        }
		/// <summary>
		/// Initializes the Entity at the given position.
		/// </summary>
		/// <param name="_position">Where the Entity is to be spawned.</param>
		public Entity(Point _position):
            base(_position) {

			entityStats = new Dictionary<string, int>(6);
			entityStats.Add("attack", 1);
			entityStats.Add("defense", 1);
			entityStats.Add("speed", 10);
			entityStats.Add("jump", 1);
			entityStats.Add("health", 10);
			entityStats.Add("scrapamount", 0);
        }

		/// <summary>
		/// Jumps into the air if not already airborne.
		/// </summary>
		public void jump() {
			((EntityState)CurrentState).Jump();
        }

		/// <summary>
		/// Moves the Entity left at its speed.
		/// </summary>
		public void moveLeft() {
			((EntityState) CurrentState).MoveLeft();
		}

		/// <summary>
		/// Moves the Entity right at its speed.
		/// </summary>
		public void moveRight() {
			((EntityState) CurrentState).MoveRight();
		}

		/// <summary>
		/// Do damage to the entity. If health is reduced to zero, kill entity.
		/// </summary>
		/// <param name="damage">Amount of damage done.</param>
		public void doDamage(int damage) {
			setStat("health", getStat("health") - damage);
			if (getStat("health") <= 0) {
				die();
			}
		}

		/// <summary>
		/// Kill the entity. Puts them in a death state or something. 
		/// </summary>
		public void die() {
			
		}

    	/// <summary>
		/// Overrides MovableObject.applyForce in order to use the Entity's speed stat.
		/// </summary>
		/// <param name="x">How much force to apply on the x axis.</param>
		/// <param name="y">How much force to apply on the y axis.</param>
		public override void applyForce(float x, float y) {
			velocity.Y = Math.Min(MaxSpeed.Y, Math.Max(-maxSpeed.Y, velocity.Y + y));
			velocity.X += x;
			if (Math.Abs(velocity.X) > getStat("speed")) {
				velocity.X = getStat("speed") * Math.Sign(velocity.X);
			}
		}

		/// <summary>
		/// Updates the state of the object. This really needs to be looked at.
		/// </summary>
		/// <param name="elapsed">Time elapsed since last tick.</param>
		public override void updateState(float elapsed) {
			if (!(CurrentState is ClimbingState)) {
				//use elapsed time, add it to the current state's elapsed time, change state depending on circumstance
				if (velocity.Y < 0) {
					currentState = getState(typeof (JumpState));
					return;
				}
				else if (velocity.Y > 0) {
					currentState = getState(typeof (FallState));
					return;
				}
				if (velocity.Y == 0 && velocity.X != 0) {
					currentState = getState(typeof (WalkState));
					return;
				}
				CurrentState = getState(typeof (StandState));
			}
		}

		/// <summary>
		/// Returns a stat from the entityStats object. Health, defense, speed, that kind of thing.
		/// </summary>
		/// <param name="statName">The name of the stat. Is converted to lowercase.</param>
		public int getStat(string statName) {
			int result;
			entityStats.TryGetValue(statName.ToLower(), out result);
			return result;
		}

		public void setStat(string statName, int _value) {
			if (entityStats.ContainsKey(statName)) {
				entityStats[statName] = _value;
			}
		}

		/// <summary>
		/// Handle collision between this Entity and Terrain.
		/// </summary>
		/// <param name="terrain">The Terrain that is being collided with.</param>
		public override void collideWith(Terrain terrain) {
			Vector2 movement = CollisionHandler.FindMovementOffset(Bounds, terrain.Bounds);

			if (movement.X != 0) {
				if (movement.X > 0) {
					setPositionX(terrain.Bounds.Right + 1);
				} else {
					setPositionX(terrain.Bounds.Left - Width - 1);
				}
				setVelocityX(0);
			}
			
			if (movement.Y != 0) {
				if (movement.Y > 0) { //colliding with ceiling
					setPositionY(terrain.Bounds.Bottom + 1);
				} else if (movement.Y < 0) {
					setPositionY(terrain.Bounds.Top - Height);
				}
				setVelocityY(0);
			}
		}

		public override void collideWith(Prop prop) {
			Terrain t = new Terrain(prop.Position);
			t.Height = prop.Height;
			t.Width = prop.Width;
			collideWith(t);
		}
		

		/// <summary>
		/// Returns the name of the Entity.
		/// </summary>
        public String Name {
            get { return name; }
            set { name = value; }
        }

		/// <summary>
		/// Returns whether or not the Entity is on a slope.
		/// </summary>
		public bool OnSlope {
			get { return onSlope; }
			set { onSlope = value; }
		}
    }
}
